package io.plus.utils.lock;

//import io.plus.utils.cache.caffeine.CaffeineCache;

import lombok.extern.slf4j.Slf4j;
import org.cache2k.Cache;
import org.cache2k.Cache2kBuilder;

import java.io.Serializable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

/**
 * 锁对象工具类
 */
@Slf4j
public class LockUtils {
    private static final String LOCK_TYPE_OBJ = "OBJ";
    private static final String LOCK_TYPE_RW = "RW";
    private static final Object defaultLockObjForObj = new Object();
    private static final Object defaultLockObjForRw = new Object();


    Cache<String, Object> LOCAL_LOCKS = new Cache2kBuilder<String, Object>() {
    }
            .name(LockUtils.class.getName() + LOCK_TYPE_OBJ)
            .eternal(true)
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .permitNullValues(true)
            .entryCapacity(1024)
            .build();
    Cache<String, Object> LOCAL_READWRITE_LOCKS = new Cache2kBuilder<String, Object>() {
    }
            .name(LockUtils.class.getName() + LOCK_TYPE_RW)
            .eternal(true)
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .permitNullValues(true)
            .entryCapacity(1024)
            .build();

    /**
     * 获取锁对象，用于synchronized(lockObj)
     */
    public Object getLockObj(String key) {
        return getObjectLock(key, null);
    }

    /**
     * 获取锁对象，用于synchronized(lockObj)
     *
     * @param key
     * @param objectLock 获取LockObj的锁，可以为null
     */
    public Object getObjectLock(String key, Object objectLock) {
        Object lock = LOCAL_LOCKS.get(key);
        if (lock == null) {
            Object ml = objectLock;
            if (ml == null) {
                ml = defaultLockObjForObj;
            }
            synchronized (ml) {
                lock = LOCAL_LOCKS.get(key);
                if (lock == null) {
                    lock = new Serializable() {
                        private static final long serialVersionUID = 255956860617836425L;
                    };
                    LOCAL_LOCKS.put(key, lock);
                }
            }
        }
        return lock;
    }

    /**
     * 获取读写锁
     *
     * @param key
     * @param myLock 获取ReentrantReadWriteLock的锁，可以为null
     */
    public ReentrantReadWriteLock getReentrantReadWriteLock(String key, Object myLock) {
        ReentrantReadWriteLock reentrantReadWriteLock = (ReentrantReadWriteLock) LOCAL_READWRITE_LOCKS.get(key);
        if (reentrantReadWriteLock == null) {
            Object ml = myLock;
            if (ml == null) {
                ml = defaultLockObjForRw;
            }
            synchronized (ml) {
                reentrantReadWriteLock = (ReentrantReadWriteLock) LOCAL_READWRITE_LOCKS.get(key);
                if (reentrantReadWriteLock == null) {
                    reentrantReadWriteLock = new ReentrantReadWriteLock();
                    LOCAL_READWRITE_LOCKS.put(key, reentrantReadWriteLock);
                }
            }
        }
        return reentrantReadWriteLock;
    }

    /**
     * 用读写锁操作<br>
     * 1、能拿到写锁的线程会执行readWriteLockHandler.write()<br>
     * 2、没拿到写锁的线程，会等待获取读锁<br>
     * <br>
     * <strong>注意：对于一些需要判断null等其它条件才执行的操作，在write()方法中建议再检查一次，这个跟double check的原理是一样的</strong><br>
     *
     * @param key
     * @param myLock               获取ReentrantReadWriteLock的锁，可以为null
     * @param readWriteLockHandler 小心：该对象的write()方法并不一定会被执行
     * @throws Exception
     */
    public void runWriteOrWaitRead(String key, Object myLock, ReadWriteLockHandler readWriteLockHandler) throws Exception {
        runWriteOrWaitRead(key, myLock, readWriteLockHandler, 180L);
    }

    /**
     * 运行write或者等待读锁<br>
     * 1、能拿到写锁的线程会执行readWriteLockHandler.write()<br>
     * 2、没拿到写锁的线程，会等待获取读锁<br>
     * <br>
     * <strong>注意：对于一些需要判断null等其它条件才执行的操作，在write()方法中建议再检查一次，这个跟double check的原理是一样的</strong><br>
     *
     * @param key
     * @param myLock               获取ReentrantReadWriteLock的锁，可以为null
     * @param readWriteLockHandler 小心：该对象的write()方法并不一定会被执行
     * @param readWaitTimeInSecond 没拿到写锁的线程，等读锁的时间，单位：秒
     */
    public void runWriteOrWaitRead(String key, Object myLock, ReadWriteLockHandler readWriteLockHandler, Long readWaitTimeInSecond) throws Exception {
        ReentrantReadWriteLock reentrantReadWriteLock = getReentrantReadWriteLock(key, myLock);
        WriteLock writeLock = reentrantReadWriteLock.writeLock();
        boolean tryWrite = writeLock.tryLock();
        if (tryWrite) {
            try {
                readWriteLockHandler.write();
//				ret.writeRet = writeRet;
            } finally {
//				ret.isWriteRunned = true;
                writeLock.unlock();
            }
        } else {
            ReadLock readLock = reentrantReadWriteLock.readLock();
            boolean tryRead = false;
            try {
                tryRead = readLock.tryLock(readWaitTimeInSecond, TimeUnit.SECONDS);
                if (tryRead) {
//					try {
//						readWriteLockHandler.read();
//						ret.readRet = readRet;
//					} finally {
//						ret.isReadRunned = true;
                    readLock.unlock();
//					}
                }
            } catch (InterruptedException e) {
                log.error(e.toString(), e);
            }
        }
//		return ret;
    }
}
